# -*- coding: utf-8 -*-
#
# The author disclaims copyright to this source code.
# In place of a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
# Any feedback will be appreciated. mailto:k-tak@void.in
#
import libsenna.libsenna as _sen


############################################################################
# Constants
############################################################################

class _Flag(object):
    __slots__ = ('Normalize', 'SplitAlpha', 'SplitDigit',
                 'SplitSymbol', 'MorphAnalyse', 'Ngram',
                 'Delimited', 'EnableSuffixSearch',
                 'DisableSuffixSearch', 'WithStore', 'WithVacuum')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('Normalize',    _sen.SEN_INDEX_NORMALIZE)
        s('SplitAlpha',   _sen.SEN_INDEX_SPLIT_ALPHA)
        s('SplitDigit' ,  _sen.SEN_INDEX_SPLIT_DIGIT)
        s('SplitSymbol' , _sen.SEN_INDEX_SPLIT_SYMBOL)
        s('MorphAnalyse', _sen.SEN_INDEX_MORPH_ANALYSE)
        s('Ngram',        _sen.SEN_INDEX_NGRAM)
        s('Delimited',    _sen.SEN_INDEX_DELIMITED)
        s('EnableSuffixSearch',  _sen.SEN_INDEX_ENABLE_SUFFIX_SEARCH)
        s('DisableSuffixSearch', _sen.SEN_INDEX_DISABLE_SUFFIX_SEARCH)
        s('WithStore',    _sen.SEN_INDEX_WITH_STORE)
        s('WithVacuum',   _sen.SEN_INDEX_WITH_VACUUM)
    
    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _SymFlag(object):
    __slots__ = ('WithSis',)

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('WithSis', _sen.SEN_SYM_WITH_SIS)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _Rc(object):
    __slots__ = ('Success', 'MemoryExhausted', 'InvalidFormat',
                 'FileOperationError', 'InvalidArgument', 'OtherError')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('Success',            _sen.sen_success)
        s('MemoryExhausted',    _sen.sen_memory_exhausted)
        s('InvalidFormat',      _sen.sen_invalid_format)
        s('FileOperationError', _sen.sen_file_operation_error)
        s('InvalidArgument',    _sen.sen_invalid_argument)
        s('OtherError',         _sen.sen_other_error)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _Encoding(object):
    __slots__ = ('Default', 'None', 'EucJp', 'Utf8',
                 'Sjis', 'Latin1', 'Koi8r')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('Default', _sen.sen_enc_default)
        s('None',    _sen.sen_enc_none)
        s('EucJp',   _sen.sen_enc_euc_jp)
        s('Utf8',    _sen.sen_enc_utf8)
        s('Sjis',    _sen.sen_enc_sjis)
        s('Latin1',  _sen.sen_enc_latin1)
        s('Koi8r',   _sen.sen_enc_koi8r)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _RecUnit(object):
    __slots__ = ('Document', 'Section', 'Position', 'UserDef', 'None')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('Document', _sen.sen_rec_document)
        s('Section',  _sen.sen_rec_section)
        s('Position', _sen.sen_rec_position)
        s('UserDef',  _sen.sen_rec_userdef)
        s('None',     _sen.sen_rec_none)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _SelOperator(object):
    __slots__ = ('Or', 'And', 'But', 'Adjust')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('Or',     _sen.sen_sel_or)
        s('And',    _sen.sen_sel_and)
        s('But',    _sen.sen_sel_but)
        s('Adjust', _sen.sen_sel_adjust)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _SelMode(object):
    __slots__ = ('Exact', 'Partial', 'Unsplit', 'Near',
                 'Similar', 'TermExtract')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('Exact',       _sen.sen_sel_exact)
        s('Partial',     _sen.sen_sel_partial)
        s('Unsplit',     _sen.sen_sel_unsplit)
        s('Near',        _sen.sen_sel_near)
        s('Similar',     _sen.sen_sel_similar)
        s('TermExtract', _sen.sen_sel_term_extract)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _SortMode(object):
    __slots__ = ('Ascending', 'Descending')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('Ascending', _sen.sen_sort_ascending)
        s('Descending', _sen.sen_sort_descending)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _SnipFlag(object):
    __slots__ = ('None', 'Normalize')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('Normalize', _sen.SEN_SNIP_NORMALIZE)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _SnipMapping(object):
    __slots__ = ('None', 'EscapeHtmlSpecials')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('None',        _sen.SEN_SNIP_MAPPING_NONE)
        s('EscapeHtmlSpecials', _sen.SEN_SNIP_MAPPING_ESCAPE_HTML_SPECIALS)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


class _LogLevel(object):
    __slots__ = ('None', 'Emerg', 'Alert', 'Crit', 'Error',
                 'Warning', 'Notice', 'Info', 'Debug', 'Dump')

    def __init__(self):
        s = lambda x,y: object.__setattr__(self, x, y)
        s('None',    _sen.sen_log_none)
        s('Emerg',   _sen.sen_log_emerg)
        s('Alert',   _sen.sen_log_alert)
        s('Crit',    _sen.sen_log_crit)
        s('Error',   _sen.sen_log_error)
        s('Warning', _sen.sen_log_warning)
        s('Notice',  _sen.sen_log_notice)
        s('Info',    _sen.sen_log_info)
        s('Debug',   _sen.sen_log_debug)
        s('Dump',    _sen.sen_log_dump)

    def __getattr__(self, name):
        raise AttributeError

    def __setattr__(self, name, value):
        raise AttributeError


Flag = _Flag()
SymFlag = _SymFlag()
Rc = _Rc()
Encoding = _Encoding()
RecUnit = _RecUnit()
SelOperator = _SelOperator()
SelMode = _SelMode()
SortMode = _SortMode()
SnipFlag = _SnipFlag()
SnipMapping = _SnipMapping()
LogLevel = _LogLevel()


############################################################################
# Exceptions
############################################################################

class PyrostError(Exception):
    def __init__(self, rc):
        self._rc = rc

    def __str__(self):
        return repr(self._rc)

    ## リザルトコードを取得
    # @return リザルトコード(Rcで定義された定数のいずれか)
    def resultcode(self):
        return self._rc


############################################################################
# Index
############################################################################

## インデックスを表すクラス
class Index(object):
    ## コンストラクタ
    # @param path オープンするインデックスへのパス。オプショナル。
    # @throws PyrostError インデックスを開けない場合
    def __init__(self, path=None):
        self._idx = None
        self._path = None
        if (path != None):
            self.open(path)

    ## インデックスが正常に開かれていればTrueを返す。
    # @return 正常に開かれていればTrue、そうでなければFalse。
    def valid(self):
        return (self._idx != None)

    ## インデックスファイルへのパスを返す。
    # @return インデックスファイルへのパス
    def path(self):
        return self._path

    ## インデックスを閉じる
    # @return リザルトコード(Rcで定義されている定数のいずれか)
    def close(self):
        rc = Rc.Success
        if (self._idx != None):
            rc = _sen.sen_index_close(self._idx)
        self._idx = None
        self._path = None
        return rc

    ## インデックスを開く
    # @param path オープンするインデックスへのパス
    # @param keys インデックスで用いるシンボル表。オプショナル。
    # @throws PyrostError
    def open(self, path, keys=None):
        self.close()
        if (keys == None):
            self._idx = _sen.sen_index_open(path)
        else:
            self._idx = _sen.sen_index_open_with_keys(path, keys)
        if (self._idx == None):
            raise PyrostError(Rc.OtherError)
        self._path = path

    ## インデックスを新しく作成する
    # @param path 作成するインデックスへのパス
    # @param flags 作成フラグ。Flagで定義された定数の組み合わせを指定。
    # @param initial_n_segments 初期に確保するセグメント数。
    # @param encoding 使用する文字コード。Encodingで定義された定数のいずれか。
    # @param keys インデックスで用いるシンボル表。オプショナル。
    # @throws PyrostError
    def create(self, path, flags, initial_n_segments, encoding, keys=None):
        self.close()
        if (keys == None):
            self._idx = _sen.sen_index_create(path, flags,
                                              initial_n_segments,
                                              encoding)
        else:
            self._idx = _sen.sen_index_create_with_keys(
                path, keys, flags, initial_n_segments, encoding)
        if (self._idx == None):
            raise PyrostError(Rc.OtherError)
        self._path = path

    ## インデックスファイルを削除する
    # @param path 削除するインデックスへのパス
    # @throws PyrostError
    @staticmethod
    def remove(path):
        rc = _sen.sen_index_remove(path)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## インデックスファイルの名前を変更する
    # @param old_name 現在の名前
    # @parma new_name 新しい名前
    # @throws PyrostError
    @staticmethod
    def rename(old_name, new_name):
        rc = _sen.sen_index_rename(old_name, new_name)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## 文書を登録/更新/削除する
    # @param key 文書のキー
    # @param oldvalue 新規登録の場合はNone、更新の場合は前に登録した際の
    #                 文書内容を指定。
    # @param newvalue 登録/更新する文書の新しい内容。削除する場合は
    #                 Noneを指定。
    # @throws PyrostError
    def upd(self, key, oldvalue, newvalue):
        rc = _sen.sen_index_upd(self._idx,
                                key,
                                oldvalue,
                                newvalue)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## 文書を登録/更新/削除する
    # @param key 文書のキー
    # @param section セクション番号。1以上を指定。
    # @param oldvalue 文書の古い内容を示すValuesクラスのインスタンスを指定。
    #                 新規登録ならNoneを指定。
    # @param newvalue 文書の新しい内容を示すValuesクラスのインスタンスを指定。
    #                 削除する場合はNoneを指定。
    # @throws PyrostError
    def update(self, key, section, oldvalue, newvalue):
        if ((oldvalue != None and isinstance(oldvalue, Values) == False) or
            (newvalue != None and isinstance(newvalue, Values) == False)):
            raise TypeError
        rc = _sen.sen_index_update(self._idx,
                                   key, section, oldvalue, newvalue)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## 文書を削除する
    # @param key 文書のキー
    # @throws PyrostError
    def delete(self, key):
        rc = _sen.sen_index_del(self._idx, key)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## 指定した文字列を含む文書を検索する
    # @param string 検索対象文字列
    # @return 文字列を含む文書が存在すれば、Recordsクラスのインスタンスを返す。
    #         存在しなければNoneを返す。
    def sel(self, string):
        rec = _sen.sen_index_sel(self._idx, string)
        if (rec == None):
            return None
        else:
            return Records(rec)

    ## 指定した文字列を含む文書を検索する
    # @param string 検索対象文字列
    # @param rec 検索結果を反映するRecordsクラスのインスタンス。
    # @param op SelOperatorで定義された定数のいずれかを指定。
    # @param mode SelModeで定義された定数のいずれかを指定。
    # @param similarity_threshold modeにSimilarを指定した場合に、
    #                             idfの閾値を指定。
    # @param max_interval modeにNearを指定した場合の閾値を指定。
    # @param weight_vector 重みベクトルを指定。使用しない場合はNoneを指定。
    # @throws PyrostError
    def select(self, string, rec, op, mode,
               similarity_threshold, max_interval,
               weight_vector):
        if (weight_vector != None and
            isinstance(weight_vector, WeightVector) == False):
            raise TypeError
        if (isinstance(rec, Records) == False):
            raise TypeError
        rc = _sen.sen_index_select(self._idx, string,
                                   rec, op, mode,
                                   similarity_threshold,
                                   max_interval,
                                   weight_vector._vector,
                                   weight_vector._size)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## インデックスの情報を取得
    # @return インデックス情報
    def info(self):
        return _sen.sen_index_info(self._idx)

    ## インデックスへの問合せを実行
    # @param query Queryクラスのインスタンス。
    # @param rec 結果を反映するRecordsクラスのインスタンス。
    # @param op SelOperatorで定義された定数のいずれか。
    # @throws PyrostError
    def execQuery(self, query, rec, op):
        if (isinstance(query, Query) == False):
            raise TypeError
        if (isinstance(rec, Records) == False):
            raise TypeError

        rc = _sen.sen_query_exec(self._idx, query._query, rec._rec, op)
        if (rc != Rc.Success):
            raise PyrostError(rc)


## 重みつきの文字列を保持するクラス
class Values(object):
    ## コンストラクタ
    # @throws PyrostError
    def __init__(self):
        self._val = _sen.sen_values_open()
        if (self._val == None):
            raise PyrostError(Rc.MemoryExhausted)

    ## Valuesを閉じる
    # @return リザルトコード。Rcで定義された定数のいずれか。
    def close(self):
        rc = Rc.Success
        if (self._val != None):
            rc = _sen.sen_values_close(self._val)
        self._val = None
        return rc

    ## 重みつきの文字列を追加する
    # @param string 文字列
    # @param weight 重み
    # @throws PyrostError
    def add(self, string, weight):
        rc = _sen.sen_values_add(self._val, string, weight)
        if (rc != Rc.Success):
            raise PyrostError(rc)


## 文書レコードの集合を表現するクラス
class Records(object):
    ## コンストラクタ
    # @param rec ユーザが使用することはない。
    def __init__(self, rec=None):
        self._rec = rec

    ## レコードイテレータを取得する。
    # @return イテレータオブジェクト。
    # @note イテレータオブジェクトは、実際にはRecordsオブジェクトそのもの。
    #       iter()呼び出し後、Recordsの現在位置は未知の状態になる。
    # @throws PyrostError
    def __iter__(self):
        if (self.valid()):
            self.rewind()
            return self
        else:
            raise PyrostError(Rc.OtherError)

    ## Recordsが正常に開かれていればTrueを返す。そうでなければFalseを返す。
    def valid(self):
        return (self._rec != None)

    ## Recordsを閉じる。
    # @return リザルトコード。Rcで定義された定数のいずれか。
    def close(self):
        rc = Rc.Success
        if (self._rec != None):
            rc = _sen.sen_records_close(self._rec)
        self._rec = None
        return rc

    ## Recordsを新しく開く
    # @param record_unit レコードの単位。RecUnitで定義された定数のいずれか。
    # @param subrec_unit サブレコードの単位。RecUnitで定義された定数の
    #                    いずれか。
    # @param max_n_subrecs サブレコードの最大の数
    # @throws PyrostError
    def open(self, record_unit, subrec_unit, max_n_subrecs):
        self.close()
        self._rec = _sen.sen_records_open(record_unit,
                                          subrec_unit,
                                          max_n_subrecs)
        if (self._rec == None):
            raise PyrostError(Rc.OtherError)

    ## Recordsに含まれる次のレコードに移動する
    # @throws StopIteration
    def next(self):
        if (_sen.sen_records_next(self._rec)):
            return self
        else:
            raise StopIteration

    ## Recordsを巻き戻す。
    # @throws PyrostError
    # @note 巻き戻したRecordsは、最初のレコードの一つ前を指している。
    #       next()を呼び出さなければ、最初のレコードを取得することはできない。
    def rewind(self):
        rc = _sen.sen_records_rewind(self._rec)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## レコードのキーを取得する。
    # @return キー文字列
    def key(self):
        return _sen.sen_records_curr_key(self._rec)

    ## レコードのスコアを取得する。
    # @return スコア
    def score(self):
        return _sen.sen_records_curr_score(self._rec)

    ## Recordsに含まれるレコード数を取得
    # @return レコード数
    def nhits(self):
        return _sen.sen_records_nhits(self._rec)

    ## Recordsの中から、キーがkeyであるものを探し、そのスコアを返す。
    # @param key キー
    # @return スコア
    def find(self, key):
        return _sen.sen_records_find(self._rec, key)

    ## ２つのRecordsの和集合を得る
    # @param another もうひとつのRecordsインスタンス。
    # @throws PyrostError
    # @note anotherは破壊され、使用できなくなる。
    #       呼出し後、レシーバであるRecordsが和集合になる。
    def union(self, another):
        if (isinstance(another, Records) == False):
            raise TypeError
        
        self._rec = _sen.sen_records_union(self._rec, another._rec)
        another._rec = None
        if (self._rec == None):
            raise PyrostError(Rc.MemoryExhausted)

    ## ２つのRecordsの差集合を得る
    # @param another もう１つのRecordsインスタンス。
    # @throws PyrostError
    # @note anotherは破壊され、使用できなくなる。
    #       呼出し後、レシーバであるRecordsが差集合になる。
    def subtract(self, another):
        if (isinstance(another, Records) == False):
            raise TypeError

        self._rec = _sen.sen_records_subtract(self._rec, another._rec)
        another._rec = None
        if (self._rec == None):
            raise PyrostError(Rc.MemoryExhausted)

    ## ２つのRecordsの共通集合を得る。
    # @param another もう１つのRecordsインスタンス
    # @throws PyrostError
    # @note anotherは破壊され、使用できなくなる。
    #       呼出し後、レシーバであるRecordsが共通集合になる。
    def intersect(self, another):
        if (isinstance(another, Records) == False):
            raise TypeError

        self._rec = _sen.sen_records_intersect(self._rec, another._rec)
        another._rec = None
        if (self._rec == None):
            raise PyrostError(Rc.MemoryExhausted)

    ## ２つのRecordsから、共通に含まれるレコードを取り除く
    # @param rec1 Recordsインスタンス
    # @param rec2 Recordsインスタンス
    # @return 共通に含まれていたレコードの数
    @staticmethod
    def difference(rec1, rec2):
        if (isinstance(rec1) == False or
            isinstance(rec2) == False):
            raise TypeError

        return _sen.sen_records_difference(rec1._rec, rec2._rec)

    ## レコードをソートする
    # @param limit ソートによって上位に集めるレコード数の最大値を指定。
    # @param mode SortModeで定義された定数のいずれかを指定。
    # @throws PyrostError
    def sort(self, limit, mode):
        rc = _sen.sen_records_sort(self._rec, limit, mode)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## レコードをグループ化する。
    # @param limit 新しく作成されるレコードのサブレコード数の最大値
    # @param mode limit個以上のサブレコードが存在する場合に、
    #             残すサブレコードを選択するための方法。
    #             SortModeで定義された定数のいずれかを指定。
    # @throws PyrostError
    def group(self, limit, mode):
        rc = _sen.sen_records_group(self._rec, limit, mode)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## 指定した条件に合致するレコードハンドルを取得
    # @param key キー
    # @param section セクション番号
    # @param pos 文書内位置
    # @return レコードハンドル、スコア、及びサブレコード数。
    #         見つからなかった場合はNone。
    def at(self, key, section, pos):
        return _sen.sen_records_at(self._rec,
                                   key, section, section)

    ## レコードハンドルで示されるレコードの情報を取得
    # @param rh レコードハンドル
    # @return キー、キーサイズ、セクション番号、文書内位置、
    #         スコア、サブレコード数
    def info(self, rh):
        return _sen.sen_record_info(self._rec, rh)

    ## レコードハンドルとインデックスで示されるサブレコードの情報を取得
    # @param rh レコードハンドル
    # @param index サブレコードインデックス
    def subrecInfo(self, rh, index):
        return _sen.sen_record_subrec_info(self._rec, rh, index)


## クエリを表現するクラス
class Query(object):
    ## コンストラクタ
    def __init__(self):
        self._query = None

    ## Queryが正常に構築されていればTrue、そうでなければFalseを返す。
    # @return Queryが正常に構築されていればTrue、そうでなければFalse。
    def valid(self):
        return (self._query != None)

    ## Queryを閉じる。
    # @return リザルトコード
    def close(self):
        rc = Rc.Success
        if (self._query != None):
            rc = _sen.sen_query_close(self._query)
        self._query = None
        return rc

    ## 新しいQueryを構築する。
    # @param string 検索文字列
    # @param default_op 既定の検索方法。SelOperatorで定義される
    #                   定数のいずれかを指定。
    # @param max_exprs Queryに含むことができる検索式の最大の数
    # @param encoding stringの文字コード
    # @throws PyrostError
    def open(self, string, default_op, max_exprs, encoding):
        self.close()
        self._query = _sen.sen_query_open(string, default_op,
                                          max_exprs, encoding)
        if (self._query == None):
            raise PyrostError(Rc.OtherError)

    ## open()において、長すぎて受け付けられなかったstringの残り部分を返す。
    # @return 受け付けられなかったstringの残り部分
    def rest(self):
        return _sen.sen_query_rest(self._query)


## シンボル表を表すクラス。
class Sym(object):
    ## コンストラクタ
    # @param path オプショナル。オープンするシンボル表のパスを指定する。
    # @throws PyrostError
    def __init__(self, path=None):
        self._sym = None
        self._path = None
        if (path != None):
            self.open(path)

    ## シンボル表が正常に開かれていればTrue、そうでなければFalseを返す。
    # @return シンボル表が正常に開かれていればTrue、そうでなければFalse。
    def valid(self):
        return (self._sym != None)

    ## 開いているシンボル表のパスを返す。
    # @return パス
    def path(self):
        return self._path

    ## 現在開いているシンボル表を閉じる
    # @return リザルトコード
    def close(self):
        rc = Rc.Success
        if (self._sym != None):
            rc = _sen.sen_sym_close(self._sym)
            self._sym = None
        return rc

    ## シンボル表を開く
    # @param path シンボル表のパス
    # @throws PyrostError
    def open(self, path):
        self.close()
        self._sym = _sen.sen_sym_open(path)
        if (self._sym == None):
            raise PyrostError(Rc.OtherError)

    ## シンボル表を新しく作成する
    # @param path 作成するシンボル表のパス
    # @param flags シンボル表作成フラグ。SymFlagで定義された定数のいずれか。
    # @param encoding シンボル表で用いる文字コード。
    #                 Encodingで定義された定数のいずれか。
    # @throws PyrostError
    def create(self, path, flags, encoding):
        self.close()
        self._sym = _sen.sen_sym_create(path, flags, encoding)
        if (self._sym == None):
            raise PyrostError(Rc.OtherError)

    ## 開いているシンボル表の情報を取得する。
    # @param シンボル表の情報
    def info (self):
        return _sen.sen_sym_info(self._sym)

    ## シンボル表を削除する。
    # @param パス
    # @throws PyrostError
    @staticmethod
    def remove(path):
        rc = _sen.sen_sym_remove(path)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## シンボル表を検索し、keyに対応するidを取得する。存在しなければ追加する。
    # @param key キー
    # @return キーに対応するid
    def get(self, key):
        return _sen.sen_sym_get(self._sym, key)

    ## シンボル表を検索し、keyに対応するidを取得する。
    # @param key キー
    # @return キーに対応するid
    # @throws PyrostError
    # @note keyが存在しない場合、PyrostErrorがスローされる。
    def at(self, key):
        sid = _sen.sen_sym_at(self._sym, key)
        if (sid == _sen.SEN_SYM_NIL):
            raise PyrostError(Rc.OtherError)

    ## シンボル表からキーを削除する
    # @param key キー
    def delete(self, key):
        return _sen.sen_sym_del(self._sym, key)

    ## シンボル表に含まれる項目数を取得する
    # @return 項目数
    def size(self):
        return _sen.sen_sym_size(self._sym)

    ## idに対応するキーを取得する
    # @param sid id
    # @return キー
    def key(self, sid):
        return _sen.sen_sym_key(self._sym, sid)

    def __preSuffixSearch(self, key, prefix):
        set = None
        if (prefix):
            set = _sen.sen_sym_prefix_search(self._sym, key)
        else:
            set = _sen.sen_sym_suffix_search(self._sym, key)

        if (set == None):
            return []

        cur = _sen.sen_set_cursor_open(set)
        if (cur == None):
            _sen.sen_set_close(set)
            return []

        eh = None
        sid = None
        ret = []
        try:
            while 1:
                eh, sid = _sen.sen_set_sym_id_cursor_next(cur)
                if (eh == None):
                    break
                ret.append(sid)
        except:
            pass

        _sen.sen_set_cursor_close(cur)
        _sen.sen_set_close(set)
        return ret;

    ## keyで始まるキーを持つ項目のidをすべて取得する
    # @param key 前方一致部分文字列
    # @return idのリスト。見つからない場合は空のリストを返す。
    def prefixSearch(self, key):
        return self.__preSuffixSearch(key, True)

    ## keyで終わるキーを持つ項目のidをすべて取得する
    # @param key 後方一致部分文字列
    # @return idのリスト。見つからない場合は空のリストを返す。
    def suffixSearch(self, key):
        return self.__preSuffixSearch(key, False)

    ## keyとキー文字列が最も長く前方一致する項目を検索し、そのidを返す。
    # @param key 前方一致部分文字列
    # @return id
    def commonPrefixSearch(self, key):
        return _sen.sen_sym_common_prefix_search(self._sym, key)

    ## idに対応する項目に関連付けられた値を取得する
    # @param sid id
    # @return idに関連付けられた整数値
    def pocketGet(self, sid):
        return _sen.sen_sym_pocket_get(self._sym, sid)

    ## idに対応する項目に、整数値を関連付ける
    # @param sid id
    # @param value 関連付ける整数値
    # @throws PyrostError
    def pocketSet(self, sid, value):
        rc = _sen.sen_sym_pocket_set(self._sym, sid, value)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## sidに対応する項目の次の項目のidを取得する。
    # @param sid id。オプショナル。省略した場合は最初のidが返される。
    # @return sidの次の項目のid。次の項目が存在しなければ、Noneが返される。
    def next(self, sid=_sen.SEN_SYM_NIL):
        sid = _sen.sen_sym_next(self._sym, sid)
        if (sid == _sen.SEN_SYM_NIL):
            return None
        else:
            return sid


## snip作成用クラス
class Snip(object):
    ## コンストラクタ
    def __init__(self):
        self._snip = None

    ## snip作成用オブジェクトを初期化する
    # @param encoding 使用する文字コード。Encodingで定義される定数の
    #        いずれかを指定。
    # @param flags フラグ。SnipFlagsで定義される定数のいずれかを指定。
    # @param width 作成されるsnipのバイト数を指定。
    # @param max_results 作成されるsnipの最大個数を指定。
    # @param defaultopentag addCond()でopentagにNoneを指定した場合に用いられる
    #        開始タグ。
    # @param defaultclosetag addCond()でclosetagにNoneを指定した場合に用いられる
    #        終了タグ。
    # @param mapping snip内の特定の文字を置換するためのマッピングを指定。
    #        現在のところ、SnipMappingで定義される定数のいずれかを指定。
    # @throws PyrostError
    def open(self, encoding, flags, width, max_results,
             defaultopentag, defaultclosetag, mapping):
        self.close()
        self._snip = _sen.sen_snip_open(encoding, flags, width, max_results,
                                        defaultopentag, defaultclosetag,
                                        mapping)
        if (self._snip == None):
            raise PyrostError(Rc.MemoryExhausted)

    ## snip作成用オブジェクトを閉じる。
    # @return リザルトコード。
    def close(self):
        rc = Rc.Success
        if (self._snip != None):
            rc = _sen.sen_snip_close(self._snip)
            self._snip = None
        return rc

    ## 検索対象文字列と、その単語の前後に付与する文字列を追加する。
    # @param keyword 検索対象文字列
    # @param opentag 検索対象文字列の前に付与する文字列。
    #        Noneを指定した場合は、open()で指定したdefaultopentagが用いられる。
    # @param closetag 検索対象文字列の後ろに付与する文字列。
    #        Noneを指定した場合は、open()で指定したdefaultclosetagが用いられる。
    # @throws PyrostError
    def addCond(self, keyword, opentag, closetag):
        rc = _sen.sen_snip_add_cond(self._snip, keyword, opentag, closetag)
        if (rc != Rc.Success):
            raise PyrostError(rc)

    ## snipを生成する。
    # @param string snipを生成する元になる文字列
    # @return 生成されたsnip個数
    # @throws PyrostError
    def execute(self, string):
        rc, nresults = _sen.sen_snip_exec(self._snip, string)
        if (rc != Rc.Success):
            raise PyrostError(rc)
        return nresults

    ## 生成されたsnipを取得する。
    # @param index 生成されたsnipのインデックス。0からexecute()の戻り値-1までの
    #        整数値。
    # @return snip文字列
    def getResult(self, index):
        return _sen.sen_snip_get_result(self._snip, index)


############################################################################
# Helpers
############################################################################

## WeightVectorのイテレータ
class WeightVectorIterator(object):
    def __init__(self, wv):
        if (isinstance(wv, WeightVector) == False):
            raise TypeError
        self._wv = wv
        self._vec = wv._vector
        self._size = wv._size
        self._cur = -1

    def __iter__(self):
        return self

    def next(self):
        self._cur += 1
        if (self._cur >= self._size):
            raise StopIteration
        return self._vec[self._cur]


## 重みベクトルを表現するクラス。Index.selectで用いる。
class WeightVector(object):
    ## コンストラクタ
    # @param size ベクトルの要素数を指定。
    def __init__(self, size):
        assert size >= 0
        self._size = size
        self._vector = _sen.WeightVector(size)

    def __len__(self):
        return self._size

    def __getitem__(self, key):
        if (isinstance(key, int) == False):
            raise TypeError
        if (not (0 <= key < self._size)):
            raise IndexError
        return self._vector[key]

    def __setitem__(self, key, value):
        if (isinstance(key, int) == False):
            raise TypeError
        if (not (0 <= key < self._size)):
            raise IndexError
        self._vector[key] = int(value)

    def __iter__(self):
        return WeightVectorIterator(self)

    ## シーケンスからWeightVectorを構築する。
    # @return WeightVectorインスタンス
    @staticmethod
    def fromSequence(seq):
        size = len(seq)
        ret = WeightVector(size)
        for i in xrange(0,size):
            ret[i] = seq[i]
        return ret

    ## WeightVectorの内容をリストとして返す。
    def toList(self):
        v = self._vector
        return [v[i] for i in xrange(0,self._size)]

    ## WeightVectorの内容をタプルとして返す。
    def toTuple(self):
        v = self._vector
        return tuple(v[i] for i in xrange(0,self._size))

    ## WeightVectorを指定した値で塗りつぶす
    # @param val 塗りつぶす値
    # @param start_index オプショナル。塗りつぶしを開始するインデックス。
    # @param num オプショナル。塗りつぶす要素の数。
    def fill(self, val, start_index=0, num=-1):
        end_index = 0
        if (num < 0):
            end_index = self._size
        else:
            end_index = start_index + num
        if (not (0 <= start_index < self._size)):
            raise IndexError
        if (end_index > self._size):
            raise IndexError
        
        v = self._vector
        for i in xrange(start_index, end_index):
            v[i] = val

